package cn.matezk.lianlian.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.crypto.digest.MD5;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.matezk.lianlian.beans.LabelPrintParams;
import cn.matezk.lianlian.dao.*;
import cn.matezk.lianlian.framework.RedisMutexLock;
import cn.matezk.lianlian.model.*;
import cn.matezk.lianlian.service.IApiPushService;
import cn.matezk.lianlian.service.IDeviceBindService;
import cn.matezk.lianlian.service.IDeviceService;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.iotexplorer.v20190423.IotexplorerClient;
import com.tencentcloudapi.iotexplorer.v20190423.models.DeleteDeviceRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;

@Service
@Slf4j
public class DeviceService implements IDeviceService {
    @Autowired
    private DeviceDao deviceDao;
    @Autowired
    private ProductDao productDao;
    @Autowired
    private DeviceStateDao deviceStateDao;
    @Autowired
    private UserDeviceDao userDeviceDao;
    @Autowired
    private IDeviceBindService bindService;
    @Autowired
    private IApiPushService apiPushService;
    @Autowired
    private SecretKeyDao secretKeyDao;
    @Autowired
    private DeviceBindDao bindDao;
    @Autowired
    private RedisMutexLock mutexLock;
    @Autowired
    private IotexplorerClient tencentClient;

    private static final List<String> PUBLIC_KEYS = Arrays.asList("poweron_reason", "iccid", "csq", "rssi", "switch_1", "switch_2", "switch_3", "switch_4", "detect_state1", "detect_state2", "detect_state3", "detect_state4");
    private static final List<String> EXT_KEYS = Arrays.asList("temperature", "mode", "fid", "standby", "guard", "ll_value");
    private static final List<String> PUSH_KEYS = Arrays.asList("iccid", "csq", "switch_1", "switch_2", "switch_3", "switch_4", "detect_state1", "detect_state2", "detect_state3", "detect_state4");

    @Override
    public void stateChanged(String deviceId, String state, Date eventTime) {
        // 插入上下线记录
        DeviceState deviceState = new DeviceState();
        deviceState.setDeviceId(deviceId);
        deviceState.setState(state);
        deviceState.setEventTime(eventTime);
        deviceStateDao.insert(deviceState);
        // 修改设备状态
        Device device = new Device();
        device.setDeviceId(deviceId);
        device.setDeviceStatus("EV_ONLINE".equals(state) ? 1 : 0);
        device.setStatusTime(eventTime);
        deviceDao.updateDeviceStatusPrimaryKey(device);
        // 推送到第三方平台
        String items[] = deviceId.split("/");
        SecretKey secretKey = secretKeyDao.deviceMatch(items[0], items[1]);
        if (ObjectUtil.isNull(secretKey) || ObjectUtil.isEmpty(secretKey.getPushUrl()))
            return;
        ApiPuh apiPuh = new ApiPuh();
        String params = JSONUtil.createObj()
                .set("msgType", "state")
                .set("requestId", apiPuh.getId())
                .set("deviceId", deviceId)
                .set("timestamp", eventTime.getTime())
                .set("payload", JSONUtil.createObj().set("state", state)
                ).toString();
        String md5 = MD5.create().digestHex(params.concat(secretKey.getSecret()));
        apiPuh.setDeviceId(deviceId);
        apiPuh.setUrl(String.format("%s?signature=%s", secretKey.getPushUrl(), md5));
        apiPuh.setParams(params);
        apiPushService.push(apiPuh);
    }

    @Override
    public void property(String deviceId, Date dataTime, JSONObject property) throws InterruptedException {
        JSONObject reported = JSONUtil.createObj();
        List<String> removeKeys = new ArrayList<>();
        JSONObject payload = JSONUtil.createObj();
        for (String key : property.keySet()) {
            reported.set(key, property.get(key));
            if (!PUBLIC_KEYS.contains(key) && !EXT_KEYS.contains(key)) {
                removeKeys.add(key);
            }
            if (PUSH_KEYS.contains(key)) {
                payload.set(key, property.get(key));
            }
        }
        for (String key : removeKeys) {
            property.remove(key);
        }
        Device device = new Device();
        device.setDeviceId(deviceId);
        device.setReported(reported);
        if (property.containsKey("poweron_reason"))
            device.setPoweronReason(property.getInt("poweron_reason"));
        if (property.containsKey("iccid")) {
            String iccid = property.getStr("iccid");
            device.setIccid(iccid);
        }
        if (property.containsKey("csq")) {
            device.setCsq(property.getInt("csq"));
        }
        if (property.containsKey("rssi")) {
            device.setCsq(property.getInt("rssi"));
        }
        if (property.containsKey("switch_1"))
            device.setSwitch1(property.getInt("switch_1"));
        if (property.containsKey("detect_state1"))
            device.setDetectState1(property.getInt("detect_state1"));
        deviceDao.updateByPrimaryKeySelective(device);

        // 物模型推送
        if (payload.isEmpty() || ObjectUtil.equals("HUDJ9SYY3Q", device.getProductId())) return;
        // 推送到第三方平台
        String items[] = deviceId.split("/");
        SecretKey secretKey = secretKeyDao.deviceMatch(items[0], items[1]);
        if (ObjectUtil.isNull(secretKey) || ObjectUtil.isEmpty(secretKey.getPushUrl()))
            return;
        ApiPuh apiPuh = new ApiPuh();
        String params = JSONUtil.createObj()
                .set("msgType", "property")
                .set("requestId", apiPuh.getId())
                .set("deviceId", deviceId)
                .set("timestamp", dataTime)
                .set("payload", payload).toString();
        String md5 = MD5.create().digestHex(params.concat(secretKey.getSecret()));
        apiPuh.setDeviceId(deviceId);
        apiPuh.setUrl(String.format("%s?signature=%s", secretKey.getPushUrl(), md5));
        apiPuh.setParams(params);
        apiPushService.push(apiPuh);
    }

    @Override
    public void propertyForward(String deviceId, Date dataTime, JSONObject property) throws InterruptedException {
        JSONObject payload = JSONUtil.createObj();
        // 物模型推送
        for (String key : property.keySet()) {
            if (PUSH_KEYS.contains(key)) {
                payload.set(key, property.get(key));
            }
        }

        if (payload.isEmpty()) return;
        // 推送到第三方平台
        String items[] = deviceId.split("/");
        SecretKey secretKey = secretKeyDao.deviceMatch(items[0], items[1]);
        if (ObjectUtil.isNull(secretKey) || ObjectUtil.isEmpty(secretKey.getPushUrl()))
            return;
//        log.info(String.format("appKey: %s 数据推送开始: %s", secretKey.getAppKey(), JSONUtil.toJsonStr(payload)));
        ApiPuh apiPuh = new ApiPuh();
        String params = JSONUtil.createObj()
                .set("msgType", "property")
                .set("requestId", apiPuh.getId())
                .set("deviceId", deviceId)
                .set("timestamp", dataTime)
                .set("payload", payload).toString();
        String md5 = MD5.create().digestHex(params.concat(secretKey.getSecret()));
        apiPuh.setDeviceId(deviceId);
        apiPuh.setUrl(String.format("%s?signature=%s", secretKey.getPushUrl(), md5));
        apiPuh.setParams(params);
        apiPushService.push(apiPuh);
    }

    @Override
    @Transactional
    public int exFactory(LabelPrintParams params) throws TencentCloudSDKException {
        Device db = deviceDao.selectByPrimaryKey(params.getDeviceId());
        Device ud = new Device();
        ud.setDeviceId(params.getDeviceId());
        ud.setProductId(params.getProductId());
        ud.setDeviceName(params.getDeviceName());
        ud.setIccid(params.getIccid());
        ud.setAmount(params.getAmount());
        Date now = new Date();
        ud.setExFactoryTime(now);
        ud.setPrintTime(now);
        ud.setFlowPkg(params.getFlowPkg());
        ud.setExpireDate(params.getExpireDate());
        ud.setLongitude(BigDecimal.ZERO);
        ud.setLatitude(BigDecimal.ZERO);
        // 查询产品
        Product product = productDao.selectByPrimaryKey(params.getProductId());
        // 查询是否存在相同设备名称的设备
        Device same = deviceDao.selectByDeviceName(params.getDeviceName());
        if (ObjectUtil.isNotNull(same) && ObjectUtil.notEqual(params.getProductId(), same.getProductId())) {
            if (Product.PLATFORM_TENCENT.equals(product.getPlatform())) {
                // 从腾讯云删除
                DeleteDeviceRequest deleteRequest = new DeleteDeviceRequest();
                deleteRequest.setProductId(same.getProductId());
                deleteRequest.setDeviceName(same.getDeviceName());
                deleteRequest.setForceDelete(Boolean.TRUE);
                tencentClient.DeleteDevice(deleteRequest);
            }
            // 从本地数据库删除
            deviceDao.deleteByPrimaryKey(same.getDeviceId());
            userDeviceDao.unbindByDeviceId(same.getDeviceId());
        }
        // 查询appKey绑定情况
        DeviceBind dbind = bindDao.selectByProductDevice(params.getProductId(), params.getDeviceName());
        if (ObjectUtil.isNotNull(dbind)) {
            if (ObjectUtil.notEqual(params.getAppKey(), dbind.getAppKey())) {
                // 解绑
                bindService.unbind(dbind.getAppKey(), params.getProductId(), params.getDeviceName());
                // 重新绑定
                if (ObjectUtil.isNotEmpty(params.getAppKey())) {
                    bindDao.bind(params.getAppKey(), params.getProductId(), params.getDeviceName());
                }
            }
        } else {
            dbind = bindDao.selectByDeviceName(params.getDeviceName());
            if (ObjectUtil.isNotNull(dbind)) {
                bindService.unbind(dbind.getAppKey(), dbind.getProductId(), dbind.getDeviceName());
            }

            // 重新绑定
            if (ObjectUtil.isNotEmpty(params.getAppKey())) {
                bindDao.bind(params.getAppKey(), params.getProductId(), params.getDeviceName());
            }
        }
        // 新增或更新
        if (ObjectUtil.isNull(db)) {
            ud.setFamilyId("");
            ud.setRoomId("");
            deviceDao.insertSelective(ud);
        } else {
            ud.setAliasName("");
            deviceDao.updateByPrimaryKeySelective(ud);
            deviceDao.updateAmount(ud.getDeviceId(), ud.getAmount());
        }

        // 查询今天打印了多少个
        int count = deviceDao.todayExFactory();
        return count;
    }

    @Override
    public JSONObject localProperty(String productId, String deviceName) {
        Device device = deviceDao.selectByPrimaryKey(String.format("%s/%s", productId, deviceName));
        return ObjectUtil.isNull(device) || ObjectUtil.isNull(device.getReported()) ? JSONUtil.createObj() : device.getReported();
    }

    @Override
    public void versionReport(String deviceId, String version) {
        Device device = new Device();
        device.setDeviceId(deviceId);
        device.setVersion(version);
        deviceDao.updateByPrimaryKeySelective(device);
    }

    @Override
    public List<Device> selectByAppKey(String appKey) {
        return deviceDao.selectByAppKey(appKey);
    }


    private boolean matchKey(String key) {
        if (key.startsWith("default_switch")
                || key.startsWith("count_down")
                || key.startsWith("threshold")
                || key.startsWith("rule")
                || ObjectUtil.equals("auto", key)
                || ObjectUtil.equals("charge_state", key)
                || ObjectUtil.equals("type", key)
        )   return false;
        return true;
    }

    private boolean matchKey(Set<String> keys) {
        if (ObjectUtil.isEmpty(keys))   return false;
        for (String key : keys) {
            if (!matchKey(key)) return false;
        }
        return true;
    }
}
